Skip to content

chore(deps): consolidate open dependency updates - #39

Merged
peter-gy merged 5 commits into
mainfrom
ptr/deps-and-cd-flow
Jun 23, 2026
Merged

chore(deps): consolidate open dependency updates#39
peter-gy merged 5 commits into
mainfrom
ptr/deps-and-cd-flow

Conversation

@peter-gy

@peter-gy peter-gy commented Jun 23, 2026

Copy link
Copy Markdown
Owner

Summary

This PR consolidates the open Dependabot dependency updates into one reviewable branch, refreshes the CI runtime setup around pnpm and uv, and officially adds Python 3.14 support.

Closes #34.
Closes #36.
Closes #37.
Closes #38.

Changes

  • Updates the JavaScript dev dependency group from chore(deps-dev): bump the dependencies group across 1 directory with 5 updates #38 and refreshes the pnpm lockfile.
  • Updates pnpm/action-setup from v4 to v6, actions/upload-pages-artifact from v4 to v5, and actions/deploy-pages from v4 to v5.
  • Removes .nvmrc and uses pnpm devEngines.runtime to install Node 24 or later during pnpm install.
  • Removes actions/setup-python from workflows and installs Python through uv python install.
  • Tests every supported Python minor from 3.9 through 3.14 in CI.
  • Adds the Python 3.14 Trove classifier.
  • Aligns the publish workflow with the build, trusted publish, artifact, and changelogithub release-note flow.
  • Fixes playground rendering so marimo updates render the current diagram and options reliably.

Validation

Validated the PR content in clean temporary worktrees with the committed lockfiles only:

  • pnpm install --frozen-lockfile
  • pnpm exec node -p "process.version" -> v26.3.1
  • pnpm format:check
  • pnpm lint
  • pnpm typecheck
  • pnpm build
  • uv python install "$(cat .python-version)"
  • uv python install 3.9 3.10 3.11 3.12 3.13 3.14
  • uv sync --python <version> --group test --no-default-groups for every version from 3.9 through 3.14
  • uv run --python <version> pytest -q -m "not e2e" for every version from 3.9 through 3.14
  • uv run ruff format --check .
  • uv run ruff check .
  • uv run ty check .
  • uv run pytest -q -m e2e
  • uv build
  • uv lock --check
  • git diff --check

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@peter-gy, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 26 minutes and 28 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses rolling per-developer review limits. Reviews become available again as older review attempts age out of the rolling limit window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 333e70fe-09d5-43cd-8077-73bbdf4bc3e8

📥 Commits

Reviewing files that changed from the base of the PR and between 06eba83 and a6aaa97.

📒 Files selected for processing (4)
  • .github/workflows/lint.yml
  • .github/workflows/playground-deploy.yml
  • .github/workflows/python-publish.yml
  • pyproject.toml
📝 Walkthrough

Walkthrough

The PR migrates all CI jobs from actions/setup-python to astral-sh/setup-uv with uv python install, upgrades pnpm to v6 and pages actions to v5, restructures the Python publish workflow into three uv-based jobs with automated release notes, fixes the widget's re-entrant render control flow with a do-while loop and renderAgain flag, adds responsive SVG CSS, refactors playground widget initialization to a single cell, and updates frontend tooling config.

Changes

CI/CD Tooling Migration and Publish Workflow Restructure

Layer / File(s) Summary
Python publish workflow restructure
.github/workflows/python-publish.yml
Renames the workflow and replaces release-build + pypi-publish jobs with three sequential jobs: build (uv build, uploads dist artifact), publish (uv publish --trusted-publishing always with id-token: write), and release-notes (changelogithub with contents: write).
Lint, deploy, and e2e workflow uv migration
.github/workflows/lint.yml, .github/workflows/playground-deploy.yml
Replaces actions/setup-python with astral-sh/setup-uv@v7 + uv python install across lint-and-build, python-tests, playwright-e2e, and deploy build jobs. Upgrades pnpm/action-setup from v4 to v6 and upload-pages-artifact/deploy-pages from v4 to v5.
Frontend tooling config updates
.nvmrc, pnpm-workspace.yaml, package.json
Removes Node version from .nvmrc, adds pnpm-workspace.yaml restricting builds to esbuild, and updates devDependencies versions plus replaces the engines/pnpm block with devEngines.runtime in package.json.

Widget Render Fix, Styling, and Playground Refactor

Layer / File(s) Summary
Widget re-entrant render loop and SVG styling
js/widget.ts, js/widget.css, js/global.d.ts
Adds renderAgain flag so concurrent update() calls during an in-progress render queue a follow-up render via do-while loop; moves isRendering reset to a finally block. Adds .d2-widget svg CSS rule (display: block, max-width: 100%, height: auto) and a *.css TypeScript module declaration.
Playground widget single-cell initialization
playground/d2_playground.py
Replaces the two-step Widget creation and mutation pattern with a single cell constructing d2_widget.Widget(get_script(), {...options}) and adds wrap=True to both mo.hstack(...) containers.
E2E test handler type signature fix
tests/e2e/test_playground_e2e.py
Updates _QuietHandler.log_message override to (self, format: str, *args: Any) -> None and imports Any from typing.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • peter-gy/d2-widget#32: Modifies the same three workflow files (lint.yml, playground-deploy.yml, python-publish.yml) with overlapping action version bumps, directly preceding this PR's uv migration on top of those same workflow structures.

Poem

🐇 Hop, hop, the CI runs with uv now,
No more setup-python — the rabbit takes a bow!
The widget loops once more when changes pile in,
SVGs stretch wide, no clipping, no chagrin.
Deploy pages soar on v5's wings so true —
Carrots for all who merge this tidy queue! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'chore(deps): consolidate open dependency updates' accurately describes the PR's primary objective of consolidating multiple Dependabot updates into a single branch.
Linked Issues check ✅ Passed All linked issues (#34, #36, #37, #38) are directly addressed: GitHub Actions are updated (deploy-pages v4→v5, upload-pages-artifact v4→v5, pnpm/action-setup v4→v6), CI workflows are modernized with uv, and dev dependencies are updated (TypeScript, esbuild, oxlint, oxfmt, @anywidget/types).
Out of Scope Changes check ✅ Passed Changes beyond dependency updates and CI modernization include widget rendering logic fixes (js/widget.ts reentrant update handling), CSS improvements (js/widget.css SVG styling), playground UX improvements (js/widget.ts, playground/d2_playground.py), and pnpm workspace configuration. These appear to be collateral improvements supporting the modernization rather than unrelated scope creep.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ptr/deps-and-cd-flow

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/playground-deploy.yml:
- Line 28: Replace the mutable version tag references in the GitHub Actions
workflow with their corresponding full commit SHAs to mitigate supply-chain
risk. For the pnpm/action-setup action on line 28, replace the `@v6` tag with its
pinned SHA. Similarly, replace the `@v5` tag for actions/upload-pages-artifact and
actions/deploy-pages on lines 41 and 59 respectively with their respective
pinned commit SHAs. Each action should reference a specific immutable commit
hash instead of a mutable version tag.

In @.github/workflows/python-publish.yml:
- Around line 21-22: The actions/checkout steps (appearing at lines 21-22,
62-63, and 85-88) persist credentials to git config by default, creating a
security risk since subsequent steps execute dependency-managed code. Add the
persist-credentials: false parameter to each actions/checkout@v6 action to
disable credential persistence. This ensures the GitHub token is not retained in
git configuration after checkout completes.
- Line 22: Replace all mutable version tags in the `uses:` statements (such as
`@v6`, `@v7`, `@v8`) with immutable commit SHAs. This applies to actions/checkout and
all other action references in the workflow file. Instead of `uses:
actions/checkout@v6`, use the format `uses:
actions/checkout@<specific-commit-sha>` where the commit SHA is the full
immutable hash of the action version you want to use.

In `@js/widget.ts`:
- Around line 71-74: The line where getRoot().innerHTML is set directly to the
svg variable lacks sanitization, creating an XSS vulnerability since the diagram
content comes from user input. Import DOMPurify at the top of the file and
modify the innerHTML assignment to sanitize the svg content by passing it
through DOMPurify.sanitize() before injection, ensuring malicious script
payloads in the SVG are neutralized before being inserted into the DOM.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2f29610d-d47a-4a6b-85e1-5254ad351b26

📥 Commits

Reviewing files that changed from the base of the PR and between fe6b555 and 06eba83.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (11)
  • .github/workflows/lint.yml
  • .github/workflows/playground-deploy.yml
  • .github/workflows/python-publish.yml
  • .nvmrc
  • js/global.d.ts
  • js/widget.css
  • js/widget.ts
  • package.json
  • playground/d2_playground.py
  • pnpm-workspace.yaml
  • tests/e2e/test_playground_e2e.py
💤 Files with no reviewable changes (1)
  • .nvmrc


- name: Setup `pnpm`
uses: pnpm/action-setup@v4
uses: pnpm/action-setup@v6

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n .github/workflows/playground-deploy.yml

Repository: peter-gy/d2-widget

Length of output: 1852


Pin deploy workflow actions to immutable SHAs.

The current tagged references (@v6, @v5) are mutable and leave the Pages pipeline open to action-retag supply-chain risk. Replace with full commit SHAs:

  • Line 28: pnpm/action-setup@v6 → pinned SHA
  • Line 41: actions/upload-pages-artifact@v5 → pinned SHA
  • Line 59: actions/deploy-pages@v5 → pinned SHA
🧰 Tools
🪛 zizmor (1.26.1)

[error] 28-28: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/playground-deploy.yml at line 28, Replace the mutable
version tag references in the GitHub Actions workflow with their corresponding
full commit SHAs to mitigate supply-chain risk. For the pnpm/action-setup action
on line 28, replace the `@v6` tag with its pinned SHA. Similarly, replace the `@v5`
tag for actions/upload-pages-artifact and actions/deploy-pages on lines 41 and
59 respectively with their respective pinned commit SHAs. Each action should
reference a specific immutable commit hash instead of a mutable version tag.

Source: Linters/SAST tools

Comment thread .github/workflows/python-publish.yml Outdated
Comment on lines 21 to 22
- name: Checkout repository
uses: actions/checkout@v6

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Disable credential persistence in each checkout step.

actions/checkout persists credentials by default. Since later steps execute dependency-managed code, keep the token out of git config.

Suggested change
- uses: actions/checkout@<sha>
+ uses: actions/checkout@<sha>
+ with:
+   persist-credentials: false

Also applies to: 62-63, 85-88

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 21-22: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 22-22: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/python-publish.yml around lines 21 - 22, The
actions/checkout steps (appearing at lines 21-22, 62-63, and 85-88) persist
credentials to git config by default, creating a security risk since subsequent
steps execute dependency-managed code. Add the persist-credentials: false
parameter to each actions/checkout@v6 action to disable credential persistence.
This ensures the GitHub token is not retained in git configuration after
checkout completes.

Source: Linters/SAST tools

Comment thread .github/workflows/python-publish.yml Outdated

steps:
- name: Checkout repository
uses: actions/checkout@v6

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find workflow actions that are not pinned to a full 40-char commit SHA
fd -e yml -e yaml .github/workflows --exec rg -nP 'uses:\s*(?!\./)[^@\s]+@(?!(?:[a-f0-9]{40})\b)'

Repository: peter-gy/d2-widget

Length of output: 530


🏁 Script executed:

#!/bin/bash
# First, let's read the python-publish.yml file to see the current state
cat -n .github/workflows/python-publish.yml

Repository: peter-gy/d2-widget

Length of output: 2883


🏁 Script executed:

#!/bin/bash
# Corrected search: find all uses: entries in the workflows directory
fd . .github/workflows -e yml -e yaml -x rg -n 'uses:' {}

Repository: peter-gy/d2-widget

Length of output: 1145


Pin all uses: actions to immutable commit SHAs.

These mutable version tags (@v6, @v7, @v8) create supply-chain risk and violate the repository's blanket pinning policy.

Suggested fix pattern
- uses: actions/checkout@v6
+ uses: actions/checkout@<40-char-commit-sha>

Apply to lines: 22, 25, 34, 42, 63, 66, 69, 86, 91.

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 21-22: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 22-22: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/python-publish.yml at line 22, Replace all mutable version
tags in the `uses:` statements (such as `@v6`, `@v7`, `@v8`) with immutable commit
SHAs. This applies to actions/checkout and all other action references in the
workflow file. Instead of `uses: actions/checkout@v6`, use the format `uses:
actions/checkout@<specific-commit-sha>` where the commit SHA is the full
immutable hash of the action version you want to use.

Source: Linters/SAST tools

Comment thread js/widget.ts
Comment on lines +71 to +74
const svg = await diagramToSvg(d2, getDiagram(), getOptions());
setSvg(svg);
getRoot().innerHTML = svg;
} catch (error: unknown) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Does @terrastruct/d2v0.1.33 guarantee thatd2.render(...)SVG output is sanitized and safe for direct insertion viaelement.innerHTML?

💡 Result:

No, @terrastruct/d2 v0.1.33 does not provide a guarantee that the output of d2.render(...) is sanitized or inherently safe for direct insertion via element.innerHTML [1][2][3]. The d2.render function generates an SVG string [1][2]. Inserting any string directly into element.innerHTML creates a potential cross-site scripting (XSS) security risk because innerHTML is an injection sink [4]. If the D2 input or the resulting SVG contains untrusted or malicious content, simply injecting it into the DOM without sanitization can lead to XSS attacks [4][5]. Security best practices for handling SVG output include: 1. Sanitization: Always pass the output through a dedicated sanitization library (such as DOMPurify) before setting it on element.innerHTML to remove potentially malicious elements or attributes [4][5]. 2. Contextual Safety: The library itself is focused on diagram generation, not security hardening of the output for arbitrary web contexts [1][3]. While D2 does include features like escaping query parameters in icons to valid SVG XML [6], this does not constitute a full security guarantee for arbitrary insertion into the DOM. 3. Content Security Policy (CSP): Implementing a strong CSP can help mitigate risks, but it is a defense-in-depth measure, not a replacement for proper sanitization when using sinks like innerHTML [7][4]. For secure insertion, you should treat the output of d2.render as untrusted input and apply appropriate sanitization routines before rendering it [4].

Citations:


🏁 Script executed:

# First, let's find and inspect the widget.ts file
fd -t f "widget.ts" --type f

Repository: peter-gy/d2-widget

Length of output: 169


🏁 Script executed:

# Check if package.json exists and look for DOMPurify dependency
fd -t f "package.json" --type f | head -5

Repository: peter-gy/d2-widget

Length of output: 169


🏁 Script executed:

# Let's also check the project structure
git ls-files | grep -E "(widget|package)" | head -20

Repository: peter-gy/d2-widget

Length of output: 342


🏁 Script executed:

# Read the widget.ts file with line numbers to verify the vulnerable code
cat -n js/widget.ts | head -100

Repository: peter-gy/d2-widget

Length of output: 3738


🏁 Script executed:

# Read package.json to check dependencies
cat package.json

Repository: peter-gy/d2-widget

Length of output: 987


Sanitize SVG before DOM injection to prevent XSS.

Line 73 directly injects SVG via innerHTML without sanitization. Since the diagram content originates from editable user input, untrusted content can flow into this path and execute script-capable SVG payloads. Use DOMPurify to sanitize the output before insertion.

Suggested fix
+import DOMPurify from "dompurify";
@@
-							const svg = await diagramToSvg(d2, getDiagram(), getOptions());
-							setSvg(svg);
-							getRoot().innerHTML = svg;
+							const svg = await diagramToSvg(d2, getDiagram(), getOptions());
+							const safeSvg = DOMPurify.sanitize(svg, {
+								USE_PROFILES: { svg: true, svgFilters: true },
+							});
+							setSvg(safeSvg);
+							getRoot().innerHTML = safeSvg;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const svg = await diagramToSvg(d2, getDiagram(), getOptions());
setSvg(svg);
getRoot().innerHTML = svg;
} catch (error: unknown) {
const svg = await diagramToSvg(d2, getDiagram(), getOptions());
const safeSvg = DOMPurify.sanitize(svg, {
USE_PROFILES: { svg: true, svgFilters: true },
});
setSvg(safeSvg);
getRoot().innerHTML = safeSvg;
} catch (error: unknown) {
🧰 Tools
🪛 ast-grep (0.44.0)

[warning] 72-72: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: getRoot().innerHTML = svg
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation

(unsafe-html-content-assignment)


[warning] 72-72: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: getRoot().innerHTML = svg
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation

(dom-content-modification)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@js/widget.ts` around lines 71 - 74, The line where getRoot().innerHTML is set
directly to the svg variable lacks sanitization, creating an XSS vulnerability
since the diagram content comes from user input. Import DOMPurify at the top of
the file and modify the innerHTML assignment to sanitize the svg content by
passing it through DOMPurify.sanitize() before injection, ensuring malicious
script payloads in the SVG are neutralized before being inserted into the DOM.

Source: Linters/SAST tools

@peter-gy
peter-gy merged commit 2d345fd into main Jun 23, 2026
10 checks passed
@peter-gy
peter-gy deleted the ptr/deps-and-cd-flow branch June 23, 2026 10:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant